class h_student {

	constructor() {
		this.decryptField = [
			'stuPwd',
			'stuZfAuthId',
			'stuName',
			'stuPhoto',
			'stuPhone',
			'stuCardId',
			'stuIdCard',
			'stuExamId',
			'stuEdu',
			'stuNation',
			'stuNation',
			'stuPolicital'
		]
	}

	checkStuId(stuId) {
	 	return this._checkStuId(stuId)
	}

	/**
	 *
	 * [public] 根据学生学号获取学生数据
	 *
	 * @param [string] stuId 学生学号 例1601170130
	 *
	 * @return [promise] stuData 学生数据
	 *
	 */

	async getStuDataByStuId(stuId, ...fields) {
		if(!this._checkStuId(stuId))
			return this.reject('-2010', 'student id invalid')
		const stuData = await this._getStuDataByStuId(stuId, ...fields)
		if(!stuData)
			return this.reject('-2009', 'student not found')
		return this.resolve(stuData)
	}

	/**
	 *
	 * [public] 根据微信用户ID获取学生数据
	 *
	 * @param [string] userId 已绑定学生身份的微信用户ID
	 *
	 * @return [promise] stuData 学生数据
	 *
	 */

	async getStuDataByUserId(userId, ...fields) {
		if(!userId)
			return this.reject('-2020', 'user id invalid')
		const stuData = await this._getStuDataByUserId(userId, ...fields)
		if(!stuData)
			return this.reject('-2009', 'student not found')
		return stuData
	}

	async checkStuBind(stuId) {
		return await this._checkStuBind(stuId)
	}

	encryptStuPwd(stuId, stuPwd) {
		return this._encryptStuPwd(stuId, stuPwd)
	}

	/**
	 *
	 * [public] 绑定学生用户
	 *
	 * @param [string] stuId 学生学号
	 * @param [object] user 用户对象
	 *
	 * @return [promise] stuData 学生数据
	 *
	 */

	async bindStuUser(stuId, stuPwd, stuInfo, user) {
		const {type, userId, session} = user
		if(!this._checkStuId(stuId))
			return this.reject('-2010', 'student id invalid')
		const stuData = await this._getStuDataByStuId(stuId, 'userId', 'stuId', 'stuName', 'stuSex', 'stuPhoto', 'stuGrade', 'stuClass', 'stuPhone', 'stuBirth', 'stuEnrollment', 'stuCardId', 'stuIdCard', 'stuExamId', 'stuEdu', 'stuNation', 'stuPolicital', 'stuRollStatus', 'stuStudyLen', 'stuStudyYearLen', 'classGrade', 'className', 'majorId')
		await this.mysql.cxxy.trans()
		const facultyData = await this.hd.department.addFaculty(stuInfo.stuFaculty)
		const majorData = await this.hd.department.addMajor(stuInfo.stuMajor, facultyData.facultyId)
		const classData = await this.hd.department.addClass(stuInfo.stuClass, majorData.majorId)
		await this.mysql.cxxy.cmt()
		await this.mysql.cxxy.trans()
		await this.hd.syllabus.addStuCourseData(stuId, stuInfo.stuSyllabus, stuInfo.courseTime, majorData.majorId, classData.classId)
		stuInfo.stuClass = classData.classId
		await this._clearClassStudentListCache(stuInfo.stuClass)
		let result = {}
		if(!stuData) {
			stuInfo.stuPwd = stuPwd
			await this._addStuData(userId, stuInfo)
			result = {
				stuId,
				stuName: stuInfo.stuName,
				stuSex: stuInfo.stuSex
			}
		}
		else if(stuData.userId != '') {
			await this.common.updateSessionType(user.session, 'normal')
			return this.reject('-2013', 'student bind id only one time')
		}
		else {
			const newStuData = await this._updateStuData(stuId, userId, stuInfo, stuData)
			result = {
				stuId: newStuData.stuId,
				stuName: newStuData.stuName,
				stuSex: newStuData.stuSex
			}
		}
		await this.mysql.cxxy.update({
			table: 'user',
			field: `userType='student'`,
			where: 'userId=$userId',
			data: {
				userId
			}
		})
		await this.mysql.cxxy.cmt()
		if(type != 'student')
			await this.common.updateSessionType(session, 'student')
		return this.resolve(result)
	}

	async untieStuUser(user) {
		const {userId, session} = user
		const result = await this._getStuDataByUserId(userId, 'stuId', 'stuPwd', 'stuZfAuthId', 'stuName', 'stuSex', 'stuPhoto', 'stuGrade', 'stuClass', 'stuPhone', 'stuBirth', 'stuEnrollment', 'stuSCardId', 'stuScoreId', 'stuCardId', 'stuIdCard', 'stuExamId', 'stuEdu', 'stuNation', 'stuPolicital', 'stuRollStatus', 'stuStudyLen', 'stuStudyYearLen', 'stuSuspend', 'stuBindDate')
		if(!result)
			return this.reject('-2043', 'user not bind student')
		const {stuId} = result
		await this.mysql.cxxy.trans()
		//更新学生解绑状态
		await this.mysql.cxxy.update({
			table: 'student',
			field: [
				`userId=''`,
				'stuUntie=1',
				'stuUntieDate=$stuUntieDate'
			],
			where: 'userId=$userId',
			data: {
				stuUntieDate: this.common.timestamp(),
				userId
			}
		})
		//更新学生绑定身份
		await this.mysql.cxxy.update({
			table: 'user',
			field: `userType=''`,
			where: `userType='student' && userId=$userId`,
			data: {
				userId
			}
		})
		await this.mysql.cxxy.cmt()
		//删除用户数据缓存
		await this._clearStuDataCacheByUserId(userId)
		await this._clearStuDataCacheByStuId(stuId)
		//删除一卡通缓存
		await this.hd.card.clearStuCardCache(userId)
		//删除用户成绩缓存
		await this.hd.score.clearStuScoreCache(userId)
		//删除用户借阅记录缓存
		await this.hd.library.clearUserLibraryLoanCache(userId)
		//删除用户评教列表缓存
		await this.hd.evaluation.clearStuEvaluationCache(userId)
		//更新用户状态为normal
		await this.common.updateSessionType(session, 'normal')
	}

	async modifyStuPwd(stuId, stuPwd) {
		const result = await this.mysql.cxxy.update({
			table: 'student',
			field: 'stuPwd=$stuPwd',
			where: 'stuId=$stuId',
			data: {
				stuPwd,
				stuId
			}
		})
		if(result == 0)
			return this.reject('-2021', 'modify student password failed')
	}

	async updateStuSuspendStatus(stuId, status) {
		const result = await this.mysql.cxxy.update({
			table: 'student',
			field: 'stuSuspend=$stuSuspend',
			where: 'stuId=$stuId',
			data: {
				stuSuspend: status ? 1 : 0,
				stuId
			}
		})
		if(result == 0)
			return this.reject('-2085', 'update student suspend status failed')
	}

	//获取班级学生数据列表
	async getClassStudentList(classId, ...fields) {
		let classStuList = await this.redis.cache.get(`classStu@${classId}`, true)
		if(!classStuList) {
			classStuList = await this.mysql.cxxy.select({
				table: 'student',
				field: fields,
				where: 'stuClass=$stuClass',
				data: {
					stuClass: classId
				},
				sort: {
					field: 'stuId',
					order: 'asc'
				}
			})
			await this.redis.cache.set(`classStu@${classId}`, classStuList, this.config.frame.sessionExpires.classStudentList)
		}
		return classStuList
	}

	async clearClassStudentListCache(classId) {
		return await this._clearClassStudentListCache(classId)
	}

	async clearStuDataCacheByStuId(stuId) {
		await this.clearStuDataCacheByStuId(stuId)
	}

	async clearStuDataCacheByUserId(userId) {
		await this._clearStuDataCacheByUserId(userId)
	}

	async _clearClassStudentListCache(classId) {
		await this.redis.cache.delete(`classStu@${classId}`)
	}

	async _checkStuBind(stuId) {
		if(!this._checkStuId(stuId))
			return this.reject('-2010', 'student id invalid')
		const stuData = await this._getStuDataByStuId(stuId, 'userId')
		return stuData && stuData.userId != '' && stuData.stuUntie == 0
	}

	async _addStuData(userId, stuData) {
		const {stuId, stuPwd, stuZfAuthId, stuName, stuSex, stuPhoto, stuGrade, stuClass, stuPhone, stuBirth, stuEnrollment, stuCardId, stuIdCard, stuExamId, stuEdu, stuNation, stuPolicital, stuRollStatus, stuStudyLen, stuStudyYearLen} = stuData
		let result
		if(await this._checkStuExists(stuId))
			result = await this.mysql.cxxy.update({
				table: 'student',
				field: [
					'userId=$userId',
					'stuPwd=$stuPwd',
					'stuZfAuthId=$stuZfAuthId',
					'stuName=$stuName',
					'stuSex=$stuSex',
					'stuPhoto=$stuPhoto',
					'stuGrade=$stuGrade',
					'stuClass=$stuClass',
					'stuPhone=$stuPhone',
					'stuBirth=$stuBirth',
					'stuEnrollment=$stuEnrollment',
					'stuCardId=$stuCardId',
					'stuIdCard=$stuIdCard',
					'stuExamId=$stuExamId',
					'stuEdu=$stuEdu',
					'stuNation=$stuNation',
					'stuPolicital=$stuPolicital',
					'stuRollStatus=$stuRollStatus',
					'stuStudyLen=$stuStudyLen',
					'stuStudyYearLen=$stuStudyYearLen',
					'stuUntie=0',
					'stuBindDate=$stuBindDate',
					'stuUntieDate=0'
				],
				where: 'stuId=$stuId',
				data: {
					userId,
					stuPwd,
					stuZfAuthId,
					stuName,
					stuSex,
					stuPhoto,
					stuGrade,
					stuClass,
					stuPhone,
					stuBirth,
					stuEnrollment,
					stuCardId,
					stuIdCard,
					stuExamId,
					stuEdu,
					stuNation,
					stuPolicital,
					stuRollStatus,
					stuStudyLen,
					stuStudyYearLen,
					stuBindDate: this.common.timestamp(),
					stuId
				}
			})
		else
			result = await this.mysql.cxxy.insert({
				table: 'student',
				field: [
					'userId',
					'stuId',
					'stuPwd',
					'stuZfAuthId',
					'stuName',
					'stuSex',
					'stuPhoto',
					'stuGrade',
					'stuClass',
					'stuPhone',
					'stuBirth',
					'stuEnrollment',
					'stuCardId',
					'stuIdCard',
					'stuExamId',
					'stuEdu',
					'stuNation',
					'stuPolicital',
					'stuRollStatus',
					'stuStudyLen',
					'stuStudyYearLen',
					'stuBindDate'
				],
				data: {
					userId,
					stuId,
					stuPwd,
					stuZfAuthId,
					stuName,
					stuSex,
					stuPhoto,
					stuGrade,
					stuClass,
					stuPhone,
					stuBirth,
					stuEnrollment,
					stuCardId,
					stuIdCard,
					stuExamId,
					stuEdu,
					stuNation,
					stuPolicital,
					stuRollStatus,
					stuStudyLen,
					stuStudyYearLen,
					stuBindDate: this.common.timestamp()
				}
			})
		if(result == 0) 
			return this.reject('-2015', 'add new student data failed')
	}

	async _updateStuData(stuId, userId, stuData, oldStuData) {
		let updateKeys = []
		stuData.userId = userId
		for(let key in oldStuData) {
			if(stuData[key] && stuData[key] != oldStuData[key]) {
				updateKeys.push(`${key}=$${key}`)
				oldStuData[key] = stuData[key]
			}
		}
		if(Object.keys(oldStuData).length > 0) {
			console.log(`student ${stuData.stuName} data is change`)
			oldStuData.stuId = stuId
			await this.mysql.cxxy.update({
				table: 'student',
				field: updateKeys,
				where: 'stuId=$stuId',
				data: oldStuData
			})
		}
		else {
			console.log(`student ${oldStuData.stuName} data is up-to-date`)
		}
		return oldStuData
	}

	/**
	 *
	 * [private] 根据学生学号获取学生数据 
	 *
	 * @param [string] stuId 学生学号 例1601170130
	 *
	 * @return [object] stuData 学生数据
	 *
	 */

	async _getStuDataByStuId(stuId, ...fields) {
		let stuData = await this.redis.user.get(`student@${stuId}`, true)
		if(!stuData) {
			stuData = await this.mysql.cxxy.select({
				table: [
					'student s',
					'class c'
				],
				field: fields || 'userId',
				where: 's.stuClass=c.classId && s.stuId=$stuId',
				data: {
					stuId
				}
			})
			stuData = stuData[0]
		}
		else {
			const keys = Object.keys(stuData)
			const fieldsArr = !fields ? [] : fields
			const noFields = fieldsArr.filter((key) => {
				return keys.indexOf(this.common.trim(key)) == -1
			})
			if(noFields.length != 0) {
				const temp = await this.mysql.cxxy.select({
					table: [
						'student s',
						'class c'
					],
					field: noFields,
					where: 's.stuClass=c.classId && s.stuId=$stuId',
					data: {
						stuId
					}
				})
				if(temp[0])
					Object.assign(stuData, temp[0])
			}
		}
		if(stuData)
			await this.redis.user.set(`student@${stuId}`, stuData, this.config.frame.sessionExpires.student)
		return stuData ? stuData : null
	}

	/**
	 *
	 * [private] 根据微信用户ID获取学生数据
	 *
	 * @param [string] userId  已绑定学生身份的微信用户ID
	 *
	 * @return [object] stuData 学生数据
	 *
	 */

	async _getStuDataByUserId(userId, ...fields) {
		let stuData = await this.redis.user.get(`student@${userId}`, true)
		if(!stuData) {
			stuData = await this.mysql.cxxy.select({
				table: [
					'student s',
					'class c'
				],
				field: fields || 'userId',
				where: 's.stuClass=c.classId && userId=$userId',
				data: {
					userId
				}
			})
			stuData = stuData[0]
		}
		else {
			const keys = Object.keys(stuData)
			const fieldsArr = !fields ? [] : fields
			const noFields = fieldsArr.filter((key) => {
				return keys.indexOf(this.common.trim(key)) == -1
			})
			if(noFields.length != 0) {
				const temp = await this.mysql.cxxy.select({
					table: [
						'student s',
						'class c'
					],
					field: noFields,
					where: 's.stuClass=c.classId && userId=$userId',
					data: {
						userId
					}
				})
				if(temp[0])
					Object.assign(stuData, temp[0])
			}
		}
		if(stuData)
			await this.redis.user.set(`student@${userId}`, stuData, this.config.frame.sessionExpires.student)
		return stuData ? stuData : null
	}

	async _clearStuDataCacheByStuId(stuId) {
		await this.redis.user.delete(`student@${stuId}`)
	}

	async _clearStuDataCacheByUserId(userId) {
		await this.redis.user.delete(`student@${userId}`)
	}

	async _checkStuExists(stuId) {
		const data = await this.mysql.cxxy.select({
			table: 'student',
			field: 'stuId',
			where: 'stuId=$stuId',
			data: {
				stuId
			}
		})
		return data.length > 0
	}

	_checkStuId(stuId) {
		return /^(1\d{9}|C1\d{9})$/.test(stuId)
	}

	_encryptStuPwd(stuId = '', stuPwd) {
		return this.crypto.sha256(this.config.frame.pwdSalt + this.crypto.sha256(stuId + stuPwd))
	}

}

module.exports = h_student